/**
 * @Title: GetMethod.java
 * @Package com.lemon.utils
 * @Description: (一句话阐述):
 * @author Ran_cc
 * @date 2018年6月3日
 * @version V1.0
 */
package com.lemon.utils;

import static org.hamcrest.CoreMatchers.nullValue;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;

/**
 * Copyright 2015 Sp42 frank@ajaxjs.com
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.util.MethodInvoker;

import com.lemon.conf.MyException;
import com.lemon.conf.content.SpringContextUtil;
import com.lemon.conf.content.ThreadTaskHelper;
import com.lemon.conf.redis.custom.BaseCacheKeyGenerator;
import com.lemon.conf.redis.custom.CustomizedRedisCache;
import com.lemon.utils.sval.StaticValues;

/**
 * 获取方法 getMethod： 获取的是类的所有共有方法，这就包括自身的所有 public 方法，和从基类继承的、从接口实现的所有 public 方法
 * 
 * @author Sp42 frank@ajaxjs.com
 */
public class MethodTypeSpy {

	private static final String fmt = "%24s:   %s\n";
	private static final Logger log = LoggerFactory.getLogger(MethodTypeSpy.class);
	private static Object bean = null;

	@Deprecated
	public static void main(String[] args) throws ClassNotFoundException , InstantiationException ,
			IllegalAccessException , NoSuchMethodException , InvocationTargetException {
		//		Method method;
		//		try {
		//			method = MethodTypeSpy.getMethod(CommodityVipCouponServiceImpl.class, "selectByList");
		//			System.out.println(method);
		//		} catch (Exception e) {
		//			//将错误信息输出到日志文件
		//			e.printStackTrace();
		//		}
		//
		//		TkCommodityVipCoupon vip = new TkCommodityVipCoupon();
		//		vip.setType(5);

		/*
		 * Object object = invokerMethod(method, vip, 1, 20);
		 * System.out.println(object);
		 */
		/*
		 * CommodityVipCouponServiceImpl methodTypeSpy = new CommodityVipCouponServiceImpl();
		 * Class<? extends CommodityVipCouponServiceImpl> cls = methodTypeSpy.getClass();
		 * System.out.println("Class："+ cls.getCanonicalName());
		 * Method[] declaredMethods = cls.getDeclaredMethods();
		 * for (Method declaredMethod : declaredMethods) {
		 * System.out.println("Method name : "+declaredMethod.getName()); //获得单独的方法名
		 * //获得完整的方法信息（包括修饰符、返回值、路径、名称、参 数、抛出值）
		 * System.out.println("toGenericString :"+declaredMethod.toGenericString());
		 * int modifiers = declaredMethod.getModifiers(); //获得修饰符
		 * System.out.println("Modifiers : "+Modifier.toString(modifiers));
		 * System.out.format(fmt, "ReturnType", declaredMethod.getReturnType()); //获得返回值
		 * System.out.format(fmt, "getGenericReturnType", declaredMethod.getGenericReturnType());//获得完整信息的返回值
		 * Class<?>[] parameterTypes = declaredMethod.getParameterTypes(); //获得参数类型
		 * Type[] genericParameterTypes = declaredMethod.getGenericParameterTypes();
		 * for (int i = 0; i < parameterTypes.length; i++) {
		 * System.out.format(fmt, "ParameterType", parameterTypes[i]);
		 * System.out.format(fmt, "GenericParameterType", genericParameterTypes[i]);
		 * }
		 * Class<?>[] exceptionTypes = declaredMethod.getExceptionTypes(); //获得异常名称
		 * Type[] genericExceptionTypes = declaredMethod.getGenericExceptionTypes();
		 * for (int i = 0; i < exceptionTypes.length; i++) {
		 * System.out.format(fmt, "ExceptionTypes", exceptionTypes[i]);
		 * System.out.format(fmt, "GenericExceptionTypes", genericExceptionTypes[i]);
		 * }
		 * Annotation[] annotations = declaredMethod.getAnnotations(); //获得注解
		 * for (Annotation annotation : annotations) {
		 * System.out.format(fmt, "Annotation", annotation);
		 * System.out.format(fmt, "AnnotationType", annotation.annotationType());
		 * }
		 * }
		 */
	}

	static String cacheNam = "";  // cachenams的值
	static String cachekY = "";  // cachename 不带时间的值
	private static Object keysel = null; //记录是自定义的key 还是用的原生的key  //默认加密key
	private static final Lock REFRESH_CACKE_LOCK = new ReentrantLock(); //初始化进程

	/**
	 * <b>Description:<em><blockquote>
	 * 开启一个线程
	 * <br>
	 * 动态方法刷新对应的cache缓存, 将cache缓存设置成当前注解中的过期时间, 刷新时间=过期时间, 这样刷新后只要有请求就会刷新.<br>
	 * 只支持有刷新和过期时间的注解方法
	 * </blockquote></em></b>
	 * 
	 * @Title: invokerGenMethod
	 * @param            <T>
	 * @param clazz      要执行方法的类
	 * @param methodName 方法名
	 * @param params     方法所携带的参数
	 * @throws Exception
	 * @author Ran_cc
	 * @date 2018年6月4日
	 */
	public static <T> void invokerGenMethod(Class<T> clazz , String methodName , Object... params) throws Exception {
		//开启一个线程 , 去刷新缓存 并将线程锁定, 其他请求阻塞
		ThreadTaskHelper.run(new Runnable() {
			@Override
			public void run() {
				try {
					Method method = getMethod(clazz , methodName , params);
					String key = "";
					if (keysel == null) {
						// 通过方法的信息重新生成key
						Object generate = new BaseCacheKeyGenerator().generate(bean , method , params);
						key = generate.toString();
					} else {
						for (int i = 0; i < params.length; i++) {
							key += params[i];
						}
					}
					String par = cachekY + "::" + key;
					REFRESH_CACKE_LOCK.lock();
					if (ThreadTaskHelper.hasRunningRefreshCacheTask(par)) {
					} else {
						ThreadTaskHelper.putRefreshCacheTask(par);
						// 开启一个线程去执行指定的方法 , 刷新指定的cache缓存
						CustomizedRedisCache.cacheSupportRefreshCacheByKey(cacheNam , key);
						ThreadTaskHelper.removeRefreshCacheTask(par);
						log.info("执行刷新方法:" + methodName);
						keysel = null;
					}
				} catch (Exception e) {
					// TODO: handle exception
					MyException.writeErr(e , "执行方法失败:" + methodName);
				} finally {
					REFRESH_CACKE_LOCK.unlock();
				}
			}
		});
	}

	/**
	 * <b>Description:<em><blockquote>
	 * 通过类名和方法名获得method的对象
	 * </blockquote></em></b>
	 * 
	 * @Title: getMethod
	 * @param            <T>
	 * @param clazz
	 * @param methodName
	 * @return
	 * @throws Exception
	 * @author Ran_cc
	 * @date 2018年6月4日
	 */
	public static <T> Method getMethod(Class<T> clazz , String methodName) throws Exception {
		Method method = getMethod(clazz , methodName , nullValue());
		return method;
	}

	/**
	 * <b>Description:<em><blockquote>
	 * 通过类名和方法名和每一个参数类型获得指定的method的对象
	 * 这样做支持获得指定的重载方法
	 * </blockquote></em></b>
	 * 
	 * @Title: getMethod
	 * @param            <T>
	 * @param clazz
	 * @param methodName
	 * @param param      可变参数
	 * @return
	 * @throws Exception
	 * @author Ran_cc
	 * @date 2018年6月4日
	 */
	@SuppressWarnings("unchecked")
	public static <T> Method getMethod(Class<T> clazz , String methodName , Object... param) throws Exception {
		Class<? extends T> cls = clazz;
		bean = SpringContextUtil.getApplicationContext().getBean(clazz);
		Method[] declaredMethods = cls.getDeclaredMethods();
		Method m = null;
		boolean flag = true;
		for (Method declaredMethod : declaredMethods) {
			if (!methodName.equals(declaredMethod.getName())) {
				continue;
			}
			// Class<?> [] parameterTypes = declaredMethod.getParameterTypes(); //获得参数类型
			Type[] genericParameterTypes = declaredMethod.getGenericParameterTypes();
			//先根据参数个数判断是否相同
			if (genericParameterTypes.length == param.length) {
				for (int i = 0; i < genericParameterTypes.length; i++) {
					//传过来的参数类型会自动装箱 , 所以要用装箱类型和本身或者子类去判断
					String typeName = param[i].getClass().getTypeName();
					String typeName2 = StringUtil.getParentType(genericParameterTypes[i].getTypeName());

					if (!typeName.equals(typeName2)) {
						flag = false;
						break;
					}
				}
				// 判断每一个类型是否相等, 如果相等就返回当前的method 对象
			}

			//重写注解中的属性的值
			Annotation[] annotations = declaredMethod.getAnnotations(); //获得注解
			for (Annotation annotation : annotations) {
				System.out.format(fmt , "Annotation" , annotation);
				System.out.format(fmt , "AnnotationType" , annotation.annotationType());

				if (annotation.annotationType().getClass().isInstance(Cacheable.class)) {
					Cacheable c = (Cacheable) annotation;
					//获取 Cacheable 这个代理实例所持有的 InvocationHandler
					InvocationHandler cc = Proxy.getInvocationHandler(c);
					// 获取 AnnotationInvocationHandler 的 memberValues 字段
					Field field = cc.getClass().getDeclaredField("memberValues");
					// 因为这个字段事 private final 修饰，所以要打开权限
					field.setAccessible(true);
					Map memberValues = (Map) field.get(cc);
					//判断是否是自定义的key , 如果是自定义的key , 那么执行的方法则是要加密的key
					Object object2 = memberValues.get("keyGenerator");
					if (object2 != null && !"".equals(object2)) {
						keysel = null;
					} else {
						keysel = "key";
					}
					String[] cacheN = (String[]) memberValues.get("cacheNames");
					String cacheNames = String.valueOf(cacheN[0]);
					String[] split = cacheNames.split(StaticValues.CACHE_SEPARATOR);
					// 修改 value 属性值
					memberValues.put("cacheNames" , new String[] {
							split[0] + StaticValues.CACHE_SEPARATOR + split[1] + StaticValues.CACHE_SEPARATOR
									+ split[1] });
					cachekY = split[0];
					cacheNam = split[0];
					for (int i = 1; i < split.length; i++) {
						cacheNam += StaticValues.CACHE_SEPARATOR + split[1];
					}
				}
			}
			if (flag) {
				m = declaredMethod;
				break;
			}
		}
		return m;
	}

	//测试用的房方法
	public static <T> Method getMethodTest(Class<T> clazz , String methodName) throws Exception {
		Class<? extends T> cls = clazz;
		System.out.println("Class：" + cls.getCanonicalName());
		//bean = SpringContextUtil.getApplicationContext().getBean(clazz);
		//		String string = bean.getClass().getName();
		//		Field [] fields = clazz.getDeclaredFields();
		//
		//		Field field = clazz.getDeclaredField("vip");// 暴力获取private修饰的成员变量
		//		Class<?> class1 = field.getType();
		//		Annotation [] annotations3 = clazz.getAnnotations();
		//		Annotation [] annotations2 = field.getAnnotations();
		//		Constructor [] cs = cls.getDeclaredConstructors();
		Method[] declaredMethods = cls.getDeclaredMethods();
		Method m = null;
		for (Method declaredMethod : declaredMethods) {
			if (!methodName.equals(declaredMethod.getName())) {
				continue;
			}
			System.out.println("m.clisee:" + declaredMethod.getDeclaringClass().getName());

			//Object obj = Class.forName(declaredMethod.getDeclaringClass().getName()).newInstance();

			System.out.println("Method name : " + declaredMethod.getName());  //获得单独的方法名
			//获得完整的方法信息（包括修饰符、返回值、路径、名称、参数、抛出值）
			System.out.println("toGenericString :" + declaredMethod.toGenericString());

			int modifiers = declaredMethod.getModifiers();      //获得修饰符
			System.out.println("Modifiers : " + Modifier.toString(modifiers));

			System.out.format(fmt , "ReturnType" , declaredMethod.getReturnType());   //获得返回值
			System.out.format(fmt , "getGenericReturnType" , declaredMethod.getGenericReturnType());//获得完整信息的返回值

			Class<?>[] parameterTypes = declaredMethod.getParameterTypes(); //获得参数类型
			Type[] genericParameterTypes = declaredMethod.getGenericParameterTypes();
			for (int i = 0; i < parameterTypes.length; i++) {
				System.out.format(fmt , "ParameterType" , parameterTypes[i]);
				System.out.format(fmt , "GenericParameterType" , genericParameterTypes[i]);
			}

			Class<?>[] exceptionTypes = declaredMethod.getExceptionTypes();     //获得异常名称
			Type[] genericExceptionTypes = declaredMethod.getGenericExceptionTypes();
			for (int i = 0; i < exceptionTypes.length; i++) {
				System.out.format(fmt , "ExceptionTypes" , exceptionTypes[i]);
				System.out.format(fmt , "GenericExceptionTypes" , genericExceptionTypes[i]);
			}

			//可以重写注解中的属性的值
			Annotation[] annotations = declaredMethod.getAnnotations(); //获得注解
			for (Annotation annotation : annotations) {
				System.out.format(fmt , "Annotation" , annotation);
				System.out.format(fmt , "AnnotationType" , annotation.annotationType());
				/*
				 * if ( annotation.annotationType().getClass().isInstance(Cacheable.class) ) {
				 * Cacheable c = (Cacheable) annotation;
				 * //获取 Cacheable 这个代理实例所持有的 InvocationHandler
				 * InvocationHandler cc = Proxy.getInvocationHandler(c);
				 * // 获取 AnnotationInvocationHandler 的 memberValues 字段
				 * Field field = cc.getClass().getDeclaredField("memberValues");
				 * // 因为这个字段事 private final 修饰，所以要打开权限
				 * field.setAccessible(true);
				 * Map memberValues = (Map) field.get(cc);
				 * String [] cacheN = (String []) memberValues.get("cacheNames");
				 * String cacheNames = String.valueOf(cacheN[0]);
				 * String [] split = cacheNames.split(StaticValues.CACHE_SEPARATOR);
				 * // 修改 value 属性值
				 * memberValues.put("cacheNames", new String [] { split[0] + StaticValues.CACHE_SEPARATOR + "7200" + StaticValues.CACHE_SEPARATOR + "7000" });
				 * Object cacheNames22 = memberValues.get("cacheNames");
				 * }
				 */
			}
			m = declaredMethod;
		}
		return m;
	}

	/**
	 * <b>Description:<em><blockquote>
	 * 执行指定的method方法
	 * </blockquote></em></b>
	 * 
	 * @Title: invokerMethod
	 * @param method
	 * @param params
	 * @return
	 * @author Ran_cc
	 * @date 2018年6月4日
	 */
	public static Object invokerMethod(Method method , Object... params) throws Exception {
		//String name = method.getDeclaringClass().getName();
		final MethodInvoker invoker = new MethodInvoker();
		invoker.setTargetObject(bean);
		invoker.setArguments(params);
		invoker.setTargetMethod(method.getName());
		invoker.prepare();
		return invoker.invoke();
	}

	private static Object OBJECT = null;


	public static <T> Object invokerMethod2(Class<T> clazz , String methodName , Object... params) throws Exception {
		//开启一个线程 , 去刷新缓存
		//并将线程锁定, 其他请求阻塞

		ThreadTaskHelper.run(new Runnable() {
			@Override
			public void run() {
				try {
					String par = "--";
					for (int i = 0; i < params.length; i++) {
						par += params[i];
					}
					REFRESH_CACKE_LOCK.lock();
					if (ThreadTaskHelper.hasRunningRefreshCacheTask(par)) {
					} else {
						ThreadTaskHelper.putRefreshCacheTask(par);
						//String name = method.getDeclaringClass().getName();
						Method method = getMethod(clazz , methodName , params);
						final MethodInvoker invoker = new MethodInvoker();
						invoker.setTargetObject(bean);
						invoker.setArguments(params);
						invoker.setTargetMethod(method.getName());
						invoker.prepare();
						ThreadTaskHelper.removeRefreshCacheTask(par);
						OBJECT = invoker.invoke();
						System.out.println(OBJECT);
						log.info("执行刷新方法:" + methodName);
					}
				} catch (Exception e) {
					// TODO: handle exception
					MyException.writeErr(e , "执行方法失败:" + methodName);
				} finally {
					REFRESH_CACKE_LOCK.unlock();
				}
			}
		});

		return OBJECT;
	}

}